package com.example.utils;

import cn.hutool.crypto.Mode;
import cn.hutool.crypto.Padding;
import cn.hutool.crypto.asymmetric.KeyType;
import cn.hutool.crypto.asymmetric.RSA;
import cn.hutool.crypto.symmetric.AES;
import com.alibaba.fastjson.JSONObject;
import org.apache.commons.codec.binary.Base64;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import java.io.ByteArrayOutputStream;
import java.nio.charset.StandardCharsets;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.SecureRandom;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.Random;

/**
 * @author: test
 * @desc: TODO
 * @Date: 2024-09-23 11:14
 **/
public class RSATestDemo {

    // 公钥
    public static final String PUBLIC_KEY = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAhEwfZ+8gs0xKIWZp16rbnxoXuGyikxRb2E8zhi1onLqDwr5VvzJKRjj57f7Z1mX2HV20crHdOyIaJJl+/JkgOQL+9gRO9bB6rFxHYHGnW08f05XNRnlZo/G0ZVg9Iq0pH3I7uHucPL9EK2wXbODn9oMxtBCTziZOr/sxBas9pzsImhc/ZUD+DteS2qeKNvL4sUXxgt66XGu24X67m9SqMtFx0EpXsT4M7UC3jpaFuBm3ZNm4zdT6vLMsb3XggtxrUpXbUkJtzfxRWb65yx31ERpIxv9pNOtXWX9H3yeb/glM0+VZBxe1CXSlnp/tFLR858xzMM6I13s7rXZY/z+2WQIDAQABB";
    // 私钥
    public static final String PRIVATE_KEY = "MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCETB9n7yCzTEohZmnXqtufGhe4bKKTFFvYTzOGLWicuoPCvlW/MkpGOPnt/tnWZfYdXbRysd07IhokmX78mSA5Av72BE71sHqsXEdgcadbTx/Tlc1GeVmj8bRlWD0irSkfcju4e5w8v0QrbBds4Of2gzG0EJPOJk6v+zEFqz2nOwiaFz9lQP4O15Lap4o28vixRfGC3rpca7bhfrub1Koy0XHQSlexPgztQLeOloW4Gbdk2bjN1Pq8syxvdeCC3GtSldtSQm3N/FFZvrnLHfURGkjG/2k061dZf0ffJ5v+CUzT5VkHF7UJdKWen+0UtHznzHMwzojXezutdlj/P7ZZAgMBAAECggEADyd1FX+zvDYJ68eCajzdCV2AyBRRjw1u89kmqsD60bEftKf+xHbrQR33qwZdR4zIY1a/e7/Z+pMOcZDPlfmoUj+AmwelT8lWU/YCCw8Zv4DRwXfQWrk9xmTCaDVyU02If1yjcJ9gJyk1ks+/4y3kBD3E7L5VX/DNZKUeWWrjFgumFwf6NQ7n2+zt/D2dW3Swuu5JcXzM30XXt0l3IIAQG0ifQ7xvj+DzLcYMdV8z8jTEC1heca1MIidMChJkBIvF+NOb/VLdinclZWIytT/IKv5WukzNPl5wcmanXGOCAECFGje3cKLDRpzdSbE2/RxuzCF73DMcZePNZ/15cNOpMQKBgQD2lfKYxTmhr+xyPVSYzlJWhDesSeS0LCgwzo4bzLmLo573S4HAvxOMZze+2TWUMgvDwloCeSapdcBADtDrU/XeB9KK4UfyoVmpdYvWDl8pjSK7OCWDlcz5G/UhZfCvNkuiOTNFdj9ghXTXsVs6VAzOi8C3H6zTA3hODO+GRNuclQKBgQCJWSfqn08LhZjGenxj2dfE8DfTzwPVSNOaXCZR7B6t6Qeg3uPTmhB7Cuu9FbFgyG0EpcrpydQTBsoQK+ilbASCbWPnjCDQ3DNabccxpNuHQH83340maJnj2wRYFvj7uZIsx1c1JM33XyF3Sj765nWOFI86+CudCU4lY904v+C9tQKBgQDXDj2vOUsjFbBMjeqq/j5XUCbR7vw0mtJOhsbvveQQwhnRKZw3Qy5EZHz8yKlu3xrdlzIzOAAdCSIrfAB/TRGAnCfmkcXS+R+eqHLVtGEQdxPUXopxMYO7usNWZna8CMl76RErQo7RmnMl7ntW6pjkxoPD8i8jNcKFp8h6vLEf5QKBgFmjil2h42DNrEu9HPm97whlDsp5zTKh+RhBE/FbfRpbf9MKJP6xChiDpB8fzUgxBpWF7sxuIK7aO34aahesS7Uj15QC7FfjntBh3SMVSQEYou7AQ8rRcb437rnfJFiFYFVBNDVG8/WCbMEB/+CYEBjOajw+Q5iVvh7A8lxbPj4RAoGAVMy0v9GCPRVc/3ZMxDIBU1LBRao1K6wxPnI6zDEtmpnXg3fTsl2RAdflUmDuo13Z/xYM3J2qQDVtT+iDnIldMzgeS8a/4/BfGV5thkMb5HnM975smW+mMhMFyqJ2tD9NkamAuyow6pHz5GXUCo2l2UlCi5J4oGXEYMdgUF++Q/Y=";

    public static void main(String[] args) throws Exception {
        // 加密
        JSONObject encryptJson = dataEncrypt("1001", "{\"phoneMd5\":\"42285c370d91759b0cb64be35d00562e\"}", PUBLIC_KEY);
        System.out.println("密文：" + encryptJson);
        // 解密
        String decryptStr = dataDecrypt(encryptJson, PRIVATE_KEY);
        System.out.println("明文：" + decryptStr);

        String  decode = decryptByPrivateKey("DQ5FBLI8k3oiduzlJpErkXNlrLt2W3G6/XHwDRGnFu8dUhGaUyBoIaYMaCsxP5SKP7d99nCwod3IJf5JWP4mGXdyup6H3agTWhl5ZedgW69AMdecsyF84iacurRSYoGGi/XGRUpFCXuYm5zrHSoIJKhRNa+DJBEEs9gMqENlI3J6bnwniVSA8Fx+nEQ3cYoPK7uX35YTAAYnHjW7HFcWE9fRFQu1yuRp1LnjrJSsFzw8bH4od16sUoAF0YZqOUzIC8yuN7T9NCK0tlGzGV1DyDxPtU39mG4mb+zI5dZDg8UqIWMnoX6qN+FXJ1il1LjNqtopInyfujLIvwv4vpbcvQ==", "MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCETB9n7yCzTEohZmnXqtufGhe4bKKTFFvYTzOGLWicuoPCvlW/MkpGOPnt/tnWZfYdXbRysd07IhokmX78mSA5Av72BE71sHqsXEdgcadbTx/Tlc1GeVmj8bRlWD0irSkfcju4e5w8v0QrbBds4Of2gzG0EJPOJk6v+zEFqz2nOwiaFz9lQP4O15Lap4o28vixRfGC3rpca7bhfrub1Koy0XHQSlexPgztQLeOloW4Gbdk2bjN1Pq8syxvdeCC3GtSldtSQm3N/FFZvrnLHfURGkjG/2k061dZf0ffJ5v+CUzT5VkHF7UJdKWen+0UtHznzHMwzojXezutdlj/P7ZZAgMBAAECggEADyd1FX+zvDYJ68eCajzdCV2AyBRRjw1u89kmqsD60bEftKf+xHbrQR33qwZdR4zIY1a/e7/Z+pMOcZDPlfmoUj+AmwelT8lWU/YCCw8Zv4DRwXfQWrk9xmTCaDVyU02If1yjcJ9gJyk1ks+/4y3kBD3E7L5VX/DNZKUeWWrjFgumFwf6NQ7n2+zt/D2dW3Swuu5JcXzM30XXt0l3IIAQG0ifQ7xvj+DzLcYMdV8z8jTEC1heca1MIidMChJkBIvF+NOb/VLdinclZWIytT/IKv5WukzNPl5wcmanXGOCAECFGje3cKLDRpzdSbE2/RxuzCF73DMcZePNZ/15cNOpMQKBgQD2lfKYxTmhr+xyPVSYzlJWhDesSeS0LCgwzo4bzLmLo573S4HAvxOMZze+2TWUMgvDwloCeSapdcBADtDrU/XeB9KK4UfyoVmpdYvWDl8pjSK7OCWDlcz5G/UhZfCvNkuiOTNFdj9ghXTXsVs6VAzOi8C3H6zTA3hODO+GRNuclQKBgQCJWSfqn08LhZjGenxj2dfE8DfTzwPVSNOaXCZR7B6t6Qeg3uPTmhB7Cuu9FbFgyG0EpcrpydQTBsoQK+ilbASCbWPnjCDQ3DNabccxpNuHQH83340maJnj2wRYFvj7uZIsx1c1JM33XyF3Sj765nWOFI86+CudCU4lY904v+C9tQKBgQDXDj2vOUsjFbBMjeqq/j5XUCbR7vw0mtJOhsbvveQQwhnRKZw3Qy5EZHz8yKlu3xrdlzIzOAAdCSIrfAB/TRGAnCfmkcXS+R+eqHLVtGEQdxPUXopxMYO7usNWZna8CMl76RErQo7RmnMl7ntW6pjkxoPD8i8jNcKFp8h6vLEf5QKBgFmjil2h42DNrEu9HPm97whlDsp5zTKh+RhBE/FbfRpbf9MKJP6xChiDpB8fzUgxBpWF7sxuIK7aO34aahesS7Uj15QC7FfjntBh3SMVSQEYou7AQ8rRcb437rnfJFiFYFVBNDVG8/WCbMEB/+CYEBjOajw+Q5iVvh7A8lxbPj4RAoGAVMy0v9GCPRVc/3ZMxDIBU1LBRao1K6wxPnI6zDEtmpnXg3fTsl2RAdflUmDuo13Z/xYM3J2qQDVtT+iDnIldMzgeS8a/4/BfGV5thkMb5HnM975smW+mMhMFyqJ2tD9NkamAuyow6pHz5GXUCo2l2UlCi5J4oGXEYMdgUF++Q/Y=");
        System.out.println("rsa解码：" + decode);
        // 使用equals判断值是否相等，不可以用==,因为== 是判断是否是引用同一个对象的意思
        if (decode.equals("RQ93aB522Y2lIngh") ){
            System.out.println("rsa解码和php一致");
        }else{
            System.out.println("rsa解码和php不一致");
        }
    }

    /**
     * 参数加密-AES加密data，RSA公钥加密AES-Key
     *
     * @param channelId 渠道id
     * @param param     待加密的参数
     * @param publicKey 公钥
     * @return ALL
     */
    public static JSONObject dataEncrypt(String channelId, String param, String publicKey) {
        // 1.生成AES密钥
        String aesKey = generateAesKey();
        // 2.构建AES加密工具
        AES aes = buildAES(aesKey);
        // 3.加密data
        String encryptBizData = aes.encryptBase64(param);
        // 4.构建RSA加密对象
        RSA rsa = new RSA(null, publicKey);
        // 5.加密AES-Key
        String aesKeyEncryptByRsa = rsa.encryptBase64(aesKey.getBytes(), KeyType.PublicKey);
        // 6.构建返回对象
        JSONObject result = new JSONObject(3);
        result.put("channelCode", channelId);
        result.put("miAesKey", aesKeyEncryptByRsa);
        result.put("bizData", encryptBizData);
        return result;
    }

    /**
     * 生成16位不重复的随机数，含数字+大小写
     * 作为AES密钥使用
     */
    private static String generateAesKey() {
        StringBuilder uid = new StringBuilder();
        // 产生16位的强随机数
        Random rd = new SecureRandom();
        // 重复执行16次，生产16为字符串
        for (int i = 0; i < 16; i++) {
            //获取不超过三的一个随机数
            int type = rd.nextInt(3);
            switch (type) {
                case 0:
                    // 获取一个不超过10的随机数 目测是纯数字
                    uid.append(rd.nextInt(10));
                    break;
                case 1:
                    // 获取一个不超过25的随机数 + 65 目测是小写字母
                    uid.append((char) (rd.nextInt(25) + 65));
                    break;
                case 2:
                    // 获取一个不超过25的随机数 目测是小写字母
                    uid.append((char) (rd.nextInt(25) + 97));
                    break;
                default:
                    break;
            }
        }
        // 转换为字符串返回
        return uid.toString();
    }

    /**
     * 构建AES加密器
     *
     * @param aesKey AESKey
     * @return AES加密器
     */
    private static AES buildAES(String aesKey) {
        // aes加密模式   模式：ecb 填充模式：pkcs5 秘钥：字节码
        return new AES(Mode.ECB, Padding.PKCS5Padding, aesKey.getBytes(StandardCharsets.UTF_8));
    }

    /**
     * 参数解密-AES解密data，RSA私钥解密AES-Key
     *
     * @param param     待解密的参数
     * @param privateKey 私钥
     * @return ALL
     */
    public static String dataDecrypt(JSONObject param, String privateKey) throws Exception {
        String requestBizData = param.getString("bizData");
        String requestMiAesKey = param.getString("miAesKey");
        String requestAesKey = decryptByPrivateKey(requestMiAesKey, privateKey);
        return decrypt(requestBizData, requestAesKey);
    }

    //rsa使用私钥解密
    public static String decryptByPrivateKey(String data, String idRsa) throws Exception {
        //首先是base64解码，解码手字节
        byte[] encryptedData = Base64.decodeBase64(data);
        //然后调用rsa私钥解密
        byte[] result = decryptByPrivateKey(encryptedData, idRsa);
        //转为 utf-8的字符串
        return new String(result, StandardCharsets.UTF_8);
    }


    //aes使用私钥解码
    private static byte[] decryptByPrivateKey(byte[] encryptedData, String idRsa) throws Exception {
        // 获取rsa实例
        Cipher cipher = Cipher.getInstance("RSA");
        //初始化 写入秘钥
        cipher.init(2, getPrivateKey(idRsa));
        //获取数据长度
        int inputLen = encryptedData.length;
        // 输出流
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        // 记录偏移量指针
        int offSet = 0;
        // 难道这个是 'AES-128-ECB' 看起来PHP的128等于java的256位秘钥
        for (int i = 0; inputLen - offSet > 0; offSet = i * 256) {
            byte[] cache;
            if (inputLen - offSet > 256) {
                cache = cipher.doFinal(encryptedData, offSet, 256);
            } else {
                cache = cipher.doFinal(encryptedData, offSet, inputLen - offSet);
            }

            out.write(cache, 0, cache.length);
            ++i;
        }
        byte[] decryptedData = out.toByteArray();
        out.close();
        return decryptedData;
    }

    //获取私钥
    private static PrivateKey getPrivateKey(String idRsa) throws NoSuchAlgorithmException, InvalidKeySpecException {
        // 首先去除空格
        PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(Base64.decodeBase64(idRsa));
        //获取key提取实例
        KeyFactory keyFactory1;
        keyFactory1 = KeyFactory.getInstance("RSA");
        //传入私钥，提取私钥值
        return keyFactory1.generatePrivate(pkcs8EncodedKeySpec);
    }

    //aes 解码
    public static String decrypt(String sSrc, String sKey) throws Exception {
        try {
            // 判断 Key 是否正确
            if (sKey == null) {
                return null;
            }
            // 判断 Key 是否为 16 位
            if (sKey.length() != 16) {
                return null;
            }
            // 获取key的字节
            byte[] raw = sKey.getBytes(StandardCharsets.UTF_8);
            // 获取key
            SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
            // 获取aes解码实例
            Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
            // 初始化实例
            cipher.init(Cipher.DECRYPT_MODE, skeySpec);
            // 先用 base64 解密
            byte[] encrypted1 = new Base64().decode(sSrc);
            try {
                // 解码
                byte[] original = cipher.doFinal(encrypted1);
                return new String(original, StandardCharsets.UTF_8);
            } catch (Exception e) {
                return null;
            }
        } catch (Exception ex) {
            return null;
        }
    }
}
